package com.maxmlr.framework.mvc;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.maxmlr.framework.annotation.Controller;
import com.maxmlr.framework.annotation.RequestMapping;
import com.maxmlr.framework.annotation.RequestParam;
import com.maxmlr.framework.aop.AopProxyUtils;
import com.maxmlr.framework.context.ApplicationContext;
import com.maxmlr.framework.context.ClassPathApplicationContext;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @ClassName DispatchServlet
 * @Description TODO
 * @Author maxiao
 * @Date 2020/3/13 下午3:26
 * @Version V1.0
 */
public class DispatchServlet extends HttpServlet {

    //handlerMapping对应关系，
    private List<HandlerMapping> handlerMappings = Lists.newArrayList();

    //
    private Map<HandlerMapping, HandlerAdapter> handlerAdapters = Maps.newConcurrentMap();

    //
    private List<ViewResolver> viewResolvers = Lists.newArrayList();

    private final Object onRefreshMonitor = new Object();

    private final String LOCATION = "contextConfigLocation";

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String requestURI = req.getRequestURI();
        String contextPath = req.getContextPath();
        String url = requestURI.replace(contextPath, "");

        handlerMappings.forEach(handlerMapping -> {
            Pattern pattern = handlerMapping.getPattern();
            Matcher matcher = pattern.matcher(url);
            if (matcher.matches()){
                HandlerAdapter handlerAdapter = handlerAdapters.get(handlerMapping);
                try {
                    ModelAndView modelAndView = handlerAdapter.handle(req, resp, handlerMapping);
                    PrintWriter writer = resp.getWriter();
                    writer.println(modelAndView);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });

    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        ApplicationContext context = new ClassPathApplicationContext(config.getInitParameter(LOCATION));
        System.out.println("init---------------");
        synchronized (this.onRefreshMonitor) {
            onRefresh(context);
        }
    }


    protected void onRefresh(ApplicationContext context) {
        initStrategies(context);
    }

    /**
     * Initialize the strategy objects that this servlet uses.
     * <p>May be overridden in subclasses in order to initialize further strategy objects.
     */
    protected void initStrategies(ApplicationContext context) {
        // =============  这里说的就是传说中的九大组件 ================
        initMultipartResolver(context);
        initLocaleResolver(context);
        initThemeResolver(context);
        // 用来保存Controller中配置的RequestMapping和Method的一个对应关系
        initHandlerMappings(context);
        // HandlerAdapters 用来动态匹配Method参数，包括类转换，动态赋值
        initHandlerAdapters(context);
        initHandlerExceptionResolvers(context);
        initRequestToViewNameTranslator(context);
        //通过ViewResolvers实现动态模板的解析
        initViewResolvers(context);
        initFlashMapManager(context);
    }

    private void initFlashMapManager(ApplicationContext context) {
    }

    private void initViewResolvers(ApplicationContext context) {
    }

    private void initRequestToViewNameTranslator(ApplicationContext context) {
    }

    private void initHandlerExceptionResolvers(ApplicationContext context) {
    }

    /**
     * @param context
     */
    private void initHandlerAdapters(ApplicationContext context) {
        if (handlerMappings.isEmpty()) {
            return;
        }
        handlerMappings.forEach(handlerMapping -> {
            Map<String, Integer> paramMapping = Maps.newHashMap();

            Method method = handlerMapping.getMethod();
            Annotation[][] parameterAnnotations = method.getParameterAnnotations();
            for (int i = 0; i < parameterAnnotations.length; i++) {
                Annotation[] annotations = parameterAnnotations[i];
                for (Annotation annotation : annotations) {
                    if (annotation instanceof RequestParam) {
                        String paramName = ((RequestParam) annotation).value();
                        if (!"".equals(paramName)) {
                            paramMapping.put(paramName, i);
                        }
                    }
                }
            }

            Class<?>[] parameterTypes = method.getParameterTypes();
            for (int i = 0; i < parameterTypes.length; i++) {
                Class<?> parameterType = parameterTypes[i];
                if (parameterType == HttpServletRequest.class
                        || parameterType == HttpServletResponse.class) {
                    paramMapping.put(parameterType.getName(), i);
                }
            }


            handlerAdapters.put(handlerMapping, new HandlerAdapter(paramMapping));
        });
    }

    /**
     * 将Controller中配置的RequestMapping和Method进行一一对应
     *
     * @param context
     */
    private void initHandlerMappings(ApplicationContext context) {
        Set<String> beanDefinitionNames = context.getBeanDefinitionNames();
        try {

            for (String beanName : beanDefinitionNames) {
                Object proxy = context.getBean(beanName);
                Object controller = AopProxyUtils.getTargetObject(proxy);
                Class<?> aClass = controller.getClass();
                if (!aClass.isAnnotationPresent(Controller.class)) {
                    continue;
                }

                String baseUrl = "";

                if (aClass.isAnnotationPresent(RequestMapping.class)) {
                    RequestMapping annotation = aClass.getAnnotation(RequestMapping.class);
                    String value = annotation.value();
                    if (!"".equals(value)) {
                        baseUrl += "/" + value;
                    }
                }
                Method[] methods = aClass.getMethods();
                for (Method method : methods) {
                    if (!method.isAnnotationPresent(RequestMapping.class)) {
                        continue;
                    }
                    RequestMapping annotation = method.getAnnotation(RequestMapping.class);
                    String value = annotation.value();
                    String url = baseUrl;
                    if (!"".equals(value)) {
                        url += "/" + value;
                    }
                    url = url.replaceAll("\\*", ".*").replaceAll("/+", "/");
                    Pattern pattern = Pattern.compile(url);
                    this.handlerMappings.add(new HandlerMapping(pattern, controller, method));
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        }


    }

    private void initThemeResolver(ApplicationContext context) {
    }

    private void initLocaleResolver(ApplicationContext context) {
    }

    private void initMultipartResolver(ApplicationContext context) {

    }
}
